package timingwheel

import (
	"context"
	"sync"
	"time"
)

type Schedule struct {
	ticker      *time.Ticker
	timingWheel *TimingWheel
	wheelCount  int
	cxt         context.Context
	tickerTimer time.Duration
	tickerMs    int
	isRunning   bool
	lock        *sync.Mutex
	jobChan     chan IJob // 写入任务缓冲队列
	jobBuffer   int
}

func (s *Schedule) init() {
	startTimingWheel := NewTimingWheel(s.wheelCount, s.tickerMs)
	// 启动时间触发器
	s.ticker = time.NewTicker(s.tickerTimer * time.Duration(s.tickerMs))
	s.timingWheel = startTimingWheel
	s.lock = &sync.Mutex{}
	s.isRunning = false
	s.jobChan = make(chan IJob, s.jobBuffer)
}

func (s *Schedule) start() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.isRunning {
		return
	}
	s.isRunning = true
	go func() {
		for job := range s.jobChan {
			s.pushTask(NewTask(job, s))
		}
	}()
	defer func() {
		s.isRunning = false
		s.ticker.Stop()
	}()
	for {
		select {
		case <-s.cxt.Done():
			return
		case <-s.ticker.C:
			s.timingWheel.forward()
		}
	}
}

func (s *Schedule) pushTask(task *Task) {
	s.timingWheel.pushTask(task)
}

func (s *Schedule) PushJob(job IJob) {
	if !s.isRunning {
		go s.start()
	}
	s.jobChan <- job
}

func NewDefaultSchedule(ctx context.Context) *Schedule {
	scheduleBuilder := NewScheduleBuilder()
	scheduleBuilder.TickerDuration = 20
	scheduleBuilder.TickerUnit = time.Millisecond
	scheduleBuilder.WheelCount = 500
	scheduleBuilder.Ctx = ctx
	scheduleBuilder.jobBuffer = 1024
	return scheduleBuilder.Build()
}

func createWheel(wheelSize int) int {
	n := 1
	for {
		n = n << 1
		if n >= wheelSize {
			return n
		}
	}
}
